package core

import (
	"github.com/hashicorp/go-hclog"
	"sync"
)

// ABA implements Asynchronous Binary Agreement protocol
type ABA struct {
	node   *Node
	logger hclog.Logger
	
	// Round-based message storage
	bvalMsgs map[int]map[int]map[int]bool // [SN][Round][Sender]Value
	auxMsgs  map[int]map[int]map[int]bool // [SN][Round][Sender]Value
	confMsgs map[int]map[int]map[int]bool // [SN][Round][Sender]Value
	
	// Decision tracking
	decisions map[int]int // [SN]Value
	decided   map[int]bool // [SN]bool
	
	// Current round for each SN
	currentRound map[int]int // [SN]Round
	
	// Threshold values
	t int // threshold = f+1
	n int // total nodes
	
	mutex sync.RWMutex
}

// NewABA creates a new ABA instance
func NewABA(node *Node) *ABA {
	return &ABA{
		node:         node,
		logger:       hclog.New(&hclog.LoggerOptions{Name: "dumbo-aba", Level: hclog.Level(node.Config.LogLevel)}),
		bvalMsgs:     make(map[int]map[int]map[int]bool),
		auxMsgs:      make(map[int]map[int]map[int]bool),
		confMsgs:     make(map[int]map[int]map[int]bool),
		decisions:    make(map[int]int),
		decided:      make(map[int]bool),
		currentRound: make(map[int]int),
		t:            node.F + 1,
		n:            node.N,
	}
}

// Start initiates ABA for a given sequence number with input value
func (a *ABA) Start(sn int, inputValue int) {
	a.mutex.Lock()
	defer a.mutex.Unlock()
	
	if a.decided[sn] {
		return // Already decided
	}
	
	// 开始ABA延迟计时
	a.node.delayMonitor.StartABA(sn)
	
	a.currentRound[sn] = 0
	a.bvalMsgs[sn] = make(map[int]map[int]bool)
	a.auxMsgs[sn] = make(map[int]map[int]bool)
	a.confMsgs[sn] = make(map[int]map[int]bool)
	
	a.logger.Info("ABA starting", "sn", sn, "input", inputValue)
	
	// Send initial bval message
	a.sendBval(sn, 0, inputValue)
}

// sendBval sends a bval message
func (a *ABA) sendBval(sn, round, value int) {
	msg := ABABvalMsg{
		SN:     sn,
		Sender: a.node.Id,
		Round:  round,
		Value:  value,
	}
	
	a.logger.Debug("ABA sending bval", "sn", sn, "round", round, "value", value)
	a.node.PlainBroadcast(ABABvalMsgTag, msg, nil)
}

// sendAux sends an aux message
func (a *ABA) sendAux(sn, round, value int) {
	msg := ABAAuxMsg{
		SN:     sn,
		Sender: a.node.Id,
		Round:  round,
		Value:  value,
	}
	
	a.logger.Debug("ABA sending aux", "sn", sn, "round", round, "value", value)
	a.node.PlainBroadcast(ABAAuxMsgTag, msg, nil)
}

// sendConf sends a conf message
func (a *ABA) sendConf(sn, round, value int) {
	msg := ABAConfMsg{
		SN:     sn,
		Sender: a.node.Id,
		Round:  round,
		Value:  value,
	}
	
	a.logger.Debug("ABA sending conf", "sn", sn, "round", round, "value", value)
	a.node.PlainBroadcast(ABAConfMsgTag, msg, nil)
}

// HandleBvalMsg processes received bval messages
func (a *ABA) HandleBvalMsg(msg *ABABvalMsg) {
	a.mutex.Lock()
	defer a.mutex.Unlock()
	
	sn := msg.SN
	round := msg.Round
	sender := msg.Sender
	value := msg.Value
	
	// Initialize maps if needed
	if a.bvalMsgs[sn] == nil {
		a.bvalMsgs[sn] = make(map[int]map[int]bool)
	}
	if a.bvalMsgs[sn][round] == nil {
		a.bvalMsgs[sn][round] = make(map[int]bool)
	}
	
	// Store bval message
	a.bvalMsgs[sn][round][sender] = (value == 1)
	
	a.logger.Debug("ABA received bval", "sn", sn, "round", round, "sender", sender, "value", value)
	
	// Check if we have t bval messages for value 0
	count0 := 0
	for _, val := range a.bvalMsgs[sn][round] {
		if !val {
			count0++
		}
	}
	
	if count0 >= a.t && !a.hasAux(sn, round, 0) {
		a.sendAux(sn, round, 0)
	}
	
	// Check if we have t bval messages for value 1
	count1 := 0
	for _, val := range a.bvalMsgs[sn][round] {
		if val {
			count1++
		}
	}
	
	if count1 >= a.t && !a.hasAux(sn, round, 1) {
		a.sendAux(sn, round, 1)
	}
}

// HandleAuxMsg processes received aux messages
func (a *ABA) HandleAuxMsg(msg *ABAAuxMsg) {
	a.mutex.Lock()
	defer a.mutex.Unlock()
	
	sn := msg.SN
	round := msg.Round
	sender := msg.Sender
	value := msg.Value
	
	// Initialize maps if needed
	if a.auxMsgs[sn] == nil {
		a.auxMsgs[sn] = make(map[int]map[int]bool)
	}
	if a.auxMsgs[sn][round] == nil {
		a.auxMsgs[sn][round] = make(map[int]bool)
	}
	
	// Store aux message
	a.auxMsgs[sn][round][sender] = (value == 1)
	
	a.logger.Debug("ABA received aux", "sn", sn, "round", round, "sender", sender, "value", value)
	
	// Check if we have 2t aux messages
	if len(a.auxMsgs[sn][round]) >= 2*a.t {
		// Count aux messages for each value
		count0 := 0
		count1 := 0
		for _, val := range a.auxMsgs[sn][round] {
			if val {
				count1++
			} else {
				count0++
			}
		}
		
		// If we have t aux messages for value 0, send conf(0)
		if count0 >= a.t && !a.hasConf(sn, round, 0) {
			a.sendConf(sn, round, 0)
		}
		
		// If we have t aux messages for value 1, send conf(1)
		if count1 >= a.t && !a.hasConf(sn, round, 1) {
			a.sendConf(sn, round, 1)
		}
	}
}

// HandleConfMsg processes received conf messages
func (a *ABA) HandleConfMsg(msg *ABAConfMsg) {
	a.mutex.Lock()
	defer a.mutex.Unlock()
	
	sn := msg.SN
	round := msg.Round
	sender := msg.Sender
	value := msg.Value
	
	// Initialize maps if needed
	if a.confMsgs[sn] == nil {
		a.confMsgs[sn] = make(map[int]map[int]bool)
	}
	if a.confMsgs[sn][round] == nil {
		a.confMsgs[sn][round] = make(map[int]bool)
	}
	
	// Store conf message
	a.confMsgs[sn][round][sender] = (value == 1)
	
	a.logger.Debug("ABA received conf", "sn", sn, "round", round, "sender", sender, "value", value)
	
	// Check if we have 2t conf messages for the same value
	count0 := 0
	count1 := 0
	for _, val := range a.confMsgs[sn][round] {
		if val {
			count1++
		} else {
			count0++
		}
	}
	
	// If we have 2t conf messages for value 0, decide 0
	if count0 >= 2*a.t && !a.decided[sn] {
		a.decide(sn, 0)
		return
	}
	
	// If we have 2t conf messages for value 1, decide 1
	if count1 >= 2*a.t && !a.decided[sn] {
		a.decide(sn, 1)
		return
	}
	
	// If we have t conf messages for value 0, start next round with 0
	if count0 >= a.t && a.currentRound[sn] == round {
		a.currentRound[sn] = round + 1
		a.sendBval(sn, round+1, 0)
		return
	}
	
	// If we have t conf messages for value 1, start next round with 1
	if count1 >= a.t && a.currentRound[sn] == round {
		a.currentRound[sn] = round + 1
		a.sendBval(sn, round+1, 1)
		return
	}
}

// decide makes a decision for the given sequence number
func (a *ABA) decide(sn, value int) {
	if a.decided[sn] {
		return
	}
	
	a.decided[sn] = true
	a.decisions[sn] = value
	
	// 结束ABA延迟计时并记录
	delay := a.node.delayMonitor.EndABA(sn)
	a.logger.Info("ABA decided", "sn", sn, "value", value, "delay_ms", delay.Milliseconds())
	
	// Send finish message
	finishMsg := ABAFinishMsg{
		SN:     sn,
		Sender: a.node.Id,
		Value:  value,
	}
	
	a.node.PlainBroadcast(ABAFinishMsgTag, finishMsg, nil)
}

// hasAux checks if we have sent aux for the given parameters
func (a *ABA) hasAux(sn, round, value int) bool {
	if a.auxMsgs[sn] == nil || a.auxMsgs[sn][round] == nil {
		return false
	}
	
	val, exists := a.auxMsgs[sn][round][a.node.Id]
	return exists && val == (value == 1)
}

// hasConf checks if we have sent conf for the given parameters
func (a *ABA) hasConf(sn, round, value int) bool {
	if a.confMsgs[sn] == nil || a.confMsgs[sn][round] == nil {
		return false
	}
	
	val, exists := a.confMsgs[sn][round][a.node.Id]
	return exists && val == (value == 1)
}

// GetDecision returns the decision for a given sequence number
func (a *ABA) GetDecision(sn int) (int, bool) {
	a.mutex.RLock()
	defer a.mutex.RUnlock()
	
	decision, exists := a.decisions[sn]
	return decision, exists
}

// IsDecided checks if a sequence number has been decided
func (a *ABA) IsDecided(sn int) bool {
	a.mutex.RLock()
	defer a.mutex.RUnlock()
	
	return a.decided[sn]
}
